package com.espian.showcaseview.drawing; import com.espian.showcaseview.ShowcaseView; import com.espian.showcaseview.utils.ShowcaseAreaCalculator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.text.DynamicLayout; import android.text.Layout; import android.text.SpannableString; import android.text.TextPaint; import android.text.TextUtils; import android.text.style.TextAppearanceSpan; /** * Draws the text as required by the ShowcaseView */ public class TextDrawerImpl implements TextDrawer { private static final int PADDING = 24; private static final int ACTIONBAR_PADDING = 66; private final TextPaint mPaintTitle; private final TextPaint mPaintDetail; private CharSequence mTitle, mDetails; private float mDensityScale; private ShowcaseAreaCalculator mCalculator; private float[] mBestTextPosition = new float[3]; private DynamicLayout mDynamicTitleLayout; private DynamicLayout mDynamicDetailLayout; private TextAppearanceSpan mTitleSpan; private TextAppearanceSpan mDetailSpan; public TextDrawerImpl(float densityScale, ShowcaseAreaCalculator calculator) { mDensityScale = densityScale; mCalculator = calculator; mPaintTitle = new TextPaint(); mPaintTitle.setAntiAlias(true); mPaintDetail = new TextPaint(); mPaintDetail.setAntiAlias(true); } @Override public void draw(Canvas canvas, boolean hasPositionChanged) { if (shouldDrawText()) { float[] textPosition = getBestTextPosition(); if (!TextUtils.isEmpty(mTitle)) { canvas.save(); if (hasPositionChanged) { mDynamicTitleLayout = new DynamicLayout(mTitle, mPaintTitle, (int) textPosition[2], Layout.Alignment.ALIGN_NORMAL, 1.0f, 1.0f, true); } canvas.translate(textPosition[0], textPosition[1]); mDynamicTitleLayout.draw(canvas); canvas.restore(); } if (!TextUtils.isEmpty(mDetails)) { canvas.save(); if (hasPositionChanged) { mDynamicDetailLayout = new DynamicLayout(mDetails, mPaintDetail, (int) textPosition[2], Layout.Alignment.ALIGN_NORMAL, 1.2f, 1.0f, true); } canvas.translate(textPosition[0], textPosition[1] + mDynamicTitleLayout.getHeight()); mDynamicDetailLayout.draw(canvas); canvas.restore(); } } } @Override public void setDetails(CharSequence details) { if (details != null) { SpannableString ssbDetail = new SpannableString(details); ssbDetail.setSpan(mDetailSpan, 0, ssbDetail.length(), 0); mDetails = ssbDetail; } } @Override public void setTitle(CharSequence title) { if (title != null) { SpannableString ssbTitle = new SpannableString(title); ssbTitle.setSpan(mTitleSpan, 0, ssbTitle.length(), 0); mTitle = ssbTitle; } } /** * Calculates the best place to position text * * @param canvasW width of the screen * @param canvasH height of the screen */ @Override public void calculateTextPosition(int canvasW, int canvasH, ShowcaseView showcaseView) { Rect showcase = showcaseView.hasShowcaseView() ? mCalculator.getShowcaseRect() : new Rect(); int[] areas = new int[4]; //left, top, right, bottom areas[0] = showcase.left * canvasH; areas[1] = showcase.top * canvasW; areas[2] = (canvasW - showcase.right) * canvasH; areas[3] = (canvasH - showcase.bottom) * canvasW; int largest = 0; for(int i = 1; i < areas.length; i++) { if(areas[i] > areas[largest]) largest = i; } // Position text in largest area switch(largest) { case 0: mBestTextPosition[0] = PADDING * mDensityScale; mBestTextPosition[1] = PADDING * mDensityScale; mBestTextPosition[2] = showcase.left - 2 * PADDING * mDensityScale; break; case 1: mBestTextPosition[0] = PADDING * mDensityScale; mBestTextPosition[1] = (PADDING + ACTIONBAR_PADDING) * mDensityScale; mBestTextPosition[2] = canvasW - 2 * PADDING * mDensityScale; break; case 2: mBestTextPosition[0] = showcase.right + PADDING * mDensityScale; mBestTextPosition[1] = PADDING * mDensityScale; mBestTextPosition[2] = (canvasW - showcase.right) - 2 * PADDING * mDensityScale; break; case 3: mBestTextPosition[0] = PADDING * mDensityScale; mBestTextPosition[1] = showcase.bottom + PADDING * mDensityScale; mBestTextPosition[2] = canvasW - 2 * PADDING * mDensityScale; break; } if(showcaseView.getConfigOptions().centerText) { // Center text vertically or horizontally switch(largest) { case 0: case 2: mBestTextPosition[1] += canvasH / 4; break; case 1: case 3: mBestTextPosition[2] /= 2; mBestTextPosition[0] += canvasW / 4; break; } } else { // As text is not centered add actionbar padding if the text is left or right switch(largest) { case 0: case 2: mBestTextPosition[1] += ACTIONBAR_PADDING * mDensityScale; break; } } } @Override public void setTitleStyling(Context context, int styleId) { mTitleSpan = new TextAppearanceSpan(context, styleId); } @Override public void setDetailStyling(Context context, int styleId) { mDetailSpan = new TextAppearanceSpan(context, styleId); } public float[] getBestTextPosition() { return mBestTextPosition; } public boolean shouldDrawText() { return !TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mDetails); } }